//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using OpenLS.Drawing;
using OpenLS.Drawing.UI;
using OpenLS.Drawing.UI.Internals;

namespace OpenLS.Chart.UI
{
    class Bar3DModelFactory: IMultiValueConverter
    {
        #region IMultiValueConverter Members

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var series = StrictConverter.Convert<ObservableCollection<BarSeries>>(values[0]);
            if (series == null)
                return null;
            var colorScheme = StrictConverter.Convert<ColorScheme>( values[1]);
            var style = StrictConverter.Convert<ChartStyle>(values[2]);
            var varyColors = (bool) values[3];
            var shape = StrictConverter.Convert( values[4], ChartShape.Box);
            var grouping = StrictConverter.Convert( values[5], BarGrouping.Standard);
            double gapWidth = System.Convert.ToDouble( values[6]);
            var yMin = (double)values[7];
            var yMax = (double)values[8];
            Debug.WriteLine("first");
            var crossesAt = StrictConverter.ValueConvert <double> (values[9]);
            var cross = StrictConverter.ValueConvert<Crosses> (values[10]);
            BarDirection direction = StrictConverter.Convert(values[11], BarDirection.Column);
            
            double floor = 0;
            if (crossesAt != null)
                floor = crossesAt.Value;
            else if (cross != null)
            {
                switch (cross.Value)
                {
                    case Crosses.Maximum:
                        floor = yMax;
                        break;
                        case Crosses.Minimum:
                        floor = yMin;
                        break;
                }
            }
            MeshGeometryFactory geometryFactory = ChartShapeTypeToMeshFactoryConverter.Convert(shape, direction);
            if (grouping == BarGrouping.Standard)
                geometryFactory = null;
            return createModelGroup(series, colorScheme, style, varyColors, grouping, geometryFactory, gapWidth, floor, shape, direction);
        }

        private static object createModelGroup(ObservableCollection<BarSeries> series, ColorScheme colorScheme, ChartStyle style, 
            bool varyColors, BarGrouping grouping, MeshGeometryFactory geometryFactory, double gapWidth, double floor, ChartShape chartShapeFromChart,
            BarDirection direction)
        {
            var result = new Model3DGroup();
            int seriesIndex = -1;
            ChartLayout layout = ChartToChartInfoConverter.GetLayout(grouping, series);
            double gapFactor = 100/(100 + gapWidth);
            double gapBefore = (1 - gapFactor)/2;
            foreach (BarSeries currentSeries in series)
            {
                Bounds yBounds = Bounds.Create(
                    Math.Min(Helper.Min(currentSeries.Values.NumericValues, x => x.Value), floor),
                    Math.Max(Helper.Max(currentSeries.Values.NumericValues, x => x.Value), floor))
                ;
           
                seriesIndex++;
                double zOffset = layout.GetZ(seriesIndex);
                var seriesGroup = new Model3DGroup();
                result.Children.Add(seriesGroup);
                SeriesElement.SetSeries(seriesGroup, currentSeries);
                ChartElement.SetObjectContext(seriesGroup, currentSeries);
                foreach (NumericValue value in currentSeries.Values.Values)
                {
                    var valueGroup = new Model3DGroup();

                    int indexToUse = varyColors ? value.Index : seriesIndex;
                    Brush brush;
                    if (currentSeries.ShapeProperties != null && currentSeries.ShapeProperties.FillProperties != null)
                        brush =
                            FillPropertiesToBrushConverter.FillPropertiesToBrush(currentSeries.ShapeProperties.FillProperties,
                                                                                 colorScheme, null); //\\
                    else
                        brush = PatternConverter.GetBrush(style, indexToUse, colorScheme);
                    Bounds valueBounds = layout.GetBounds(value, currentSeries, floor);
                    Bounds xBound = Bounds.Create(value.Index + gapBefore, value.Index + 1 - gapBefore);
                    Bounds categoryBounds = layout.GetXBounds(xBound, currentSeries.Index, series.Count);
                    Bounds3D bounds3D = GetBounds3D(gapBefore, categoryBounds, valueBounds, zOffset, direction);
                    MeshGeometryFactory f = geometryFactory;
                    if (f == null)
                    {
                        ChartShape shape = currentSeries.Shape != null ? currentSeries.Shape.Value : chartShapeFromChart;
                        f = ChartShapeTypeToMeshFactoryConverter.Convert(shape, direction);
                    }
                    var mesh = new GeometryModel3D(f.CreateGeometry(bounds3D, yBounds, floor), 
                                                               new DiffuseMaterial(brush));
                    SeriesElement.SetSeries(mesh, currentSeries);
                    ChartElement.SetObjectContext(mesh, currentSeries);
                    valueGroup.Children.Add(mesh);
                    seriesGroup.Children.Add(valueGroup);
                }
            }
            return result;
        }

        private static Bounds3D GetBounds3D(double gapBefore, Bounds categoryBounds, Bounds valueBounds, double zOffset, BarDirection direction)
        {
            switch (direction)
            {
                case BarDirection.Column:

                    return new Bounds3D
                               {
                                   X = categoryBounds,
                                   Y = valueBounds,
                                   Z = Bounds.Create(-zOffset - 1 + gapBefore, -zOffset - gapBefore)
                               };
                default:
                    return new Bounds3D
                               {
                                   X = valueBounds,
                                   Y = categoryBounds,
                                   Z = Bounds.Create(-zOffset - 1 + gapBefore, -zOffset - gapBefore)
                               };
            }
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }

        #endregion
    }
}
